<?php

declare(strict_types=1);

namespace NanQi\Hope\Aspect;

use Hyperf\Cache\AnnotationManager;
use Hyperf\Cache\Helper\StringHelper;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;
use NanQi\Hope\Annotation\Lockable;
use NanQi\Hope\Base\BaseAspect;
use NanQi\Hope\Service\LockService;

/**
 * @Aspect
 */
class LockableAspect extends BaseAspect
{
    public $annotations = [
        Lockable::class,
    ];

    /**
     * @var AnnotationManager
     */
    protected $annotationManager;

    /**
     * @var LockService
     */
    protected $lockService;

    public function __construct(AnnotationManager $annotationManager,
                                LockService $lockService)
    {
        $this->annotationManager = $annotationManager;
        $this->lockService = $lockService;
    }

    /**
     * @param ProceedingJoinPoint $proceedingJoinPoint
     * @return mixed
     * @throws \Throwable
     */
    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
        /** @var Lockable $annotation */
        $annotation = $this->getAnnotation($proceedingJoinPoint, Lockable::class);

        $cacheKey = $annotation->prefix;
        if ($annotation->value) {
            $arguments = $proceedingJoinPoint->arguments['keys'];
            $cacheKey = StringHelper::format($annotation->prefix, $arguments, $annotation->value);

            if (strlen($cacheKey) > 64) {
                $this->getLog()->warning('The redis key length is too long. The key is ' . $cacheKey);
            }
        }

        $uSleep = 0;
        if ($annotation->isWait) {
            $uSleep = $annotation->uSleep < 20 ? 20 : $annotation->uSleep;
        }

        $flg = $this->lockService->lock($cacheKey, $annotation->ttl, $uSleep);
        if (!$flg) {
            $this->errorBusy();
        }

        try {
            $res = $proceedingJoinPoint->process();
        } finally {
            $this->lockService->unlock($cacheKey);
        }

        return $res;
    }
}
